/**
 * @file bull_hal_net_https.c
 * @brief 
 * @author yejing (yejh@gongniu.cn)
 * @version 1.0
 * @date 2024-05-23
 * 
 * Copyright 2024-2025 GongNiu Inc. All Rights Reserved.
 * 
 */

#include "bull_hal_net_https.h"

#include <esp_http_client.h>
#include "esp_log.h"
#include "esp_crt_bundle.h"

#define STR_HTTP_CLIENT_CMD_CODE(S) \
    ((S) == HTTP_EVENT_ERROR ? "HTTP_EVENT_ERROR" : \
    ((S) == HTTP_EVENT_ON_CONNECTED ? "HTTP_EVENT_ON_CONNECTED" : \
    ((S) == HTTP_EVENT_ON_HEADER ? "HTTP_EVENT_ON_HEADER" : \
    ((S) == HTTP_EVENT_ON_DATA ? "HTTP_EVENT_ON_DATA" : \
    ((S) == HTTP_EVENT_ON_FINISH ? "HTTP_EVENT_ON_FINISH" : \
    ((S) == HTTP_EVENT_DISCONNECTED ? "HTTP_EVENT_DISCONNECTED" : \
    "Unknown"))))))



static const char* HAL_HTTPS = "hal_http_net";


/**
 * @brief https 事件回调函数
 * 
 * @param evt 
 * @return OPERATE_RET 
 */
static OPERATE_RET bull_hal_https_event_handler(esp_http_client_event_t *evt)
{

    // ESP_LOGI( HAL_HTTPS, "esp_http_client_event_t:%s", STR_HTTP_CLIENT_CMD_CODE(evt->event_id) );

    BULL_HTTPS_REGISTER_T *p_https_register = NULL;

    switch(evt->event_id) {
        //错误事件
        case HTTP_EVENT_ERROR:    
            if ( evt->user_data ){

                p_https_register = (BULL_HTTPS_REGISTER_T *)evt->user_data;
                p_https_register->data_recv_cb( evt->event_id, evt->data, evt->data_len, 0, p_https_register );
            }
            break;

        //连接成功事件
        case HTTP_EVENT_ON_CONNECTED: 
            break;

        //发送头事件
        case HTTP_EVENT_HEADER_SENT: 

            break;

        //接收头事件
        case HTTP_EVENT_ON_HEADER:    

            // ESP_LOGI( HAL_HTTPS, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value );
            break;

        //接收数据事件
        case HTTP_EVENT_ON_DATA:   {
            int total_len = 0;
            if ( esp_http_client_is_chunked_response(evt->client) ){

                esp_http_client_get_chunk_length(evt->client,&total_len);
            }else{

                total_len = esp_http_client_get_content_length(evt->client);
            }
            
            // ESP_LOGE( HAL_HTTPS, "esp_http_client_get_content_length:%d", (int)esp_http_client_get_content_length(evt->client) );

            // ESP_LOGI( HAL_HTTPS, "[%d]:%s", evt->data_len,  (char*)evt->data );
            ESP_LOGI( HAL_HTTPS, " total_len:%d evt->data_len:%d",  total_len, evt->data_len );
            if ( evt->user_data ){

                p_https_register = (BULL_HTTPS_REGISTER_T *)evt->user_data;
                p_https_register->data_recv_cb( evt->event_id, evt->data, evt->data_len, total_len, p_https_register );
            }

        }break;

        //会话完成事件
        case HTTP_EVENT_ON_FINISH:{
            if ( evt->user_data ){

                p_https_register = (BULL_HTTPS_REGISTER_T *)evt->user_data;
                p_https_register->data_recv_cb( evt->event_id, evt->data, evt->data_len, 0, p_https_register );
            }
        }break;

        //断开事件
        case HTTP_EVENT_DISCONNECTED:{

            if ( evt->user_data ){

                p_https_register = (BULL_HTTPS_REGISTER_T *)evt->user_data;
                p_https_register->data_recv_cb( evt->event_id, evt->data, evt->data_len, 0, p_https_register );
            }

        }break;

        default:
        break;
    }

    return ESP_OK;
}


/**
 * @brief 执行HTTPS客户端操作
 * 
 * 该函数用于通过HTTPS协议与服务器进行通信。它会发起一个HTTPS请求，并返回请求的结果。
 * 
 * @param https_client_handle 指向HTTPS客户端句柄的指针。该句柄用于标识唯一的HTTPS客户端实例。
 * @return OPERATE_RET 函数执行的状态码。成功时返回OPRT_OK，失败时返回其他错误码。
 */
OPERATE_RET bull_hal_net_https_client_perform( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_perform( (esp_http_client_handle_t)https_client_handle ) );
}

/**
 * @brief 该函数用于取消正在进行的HTTP请求。
 * 它通过关闭当前套接字并使用相同的esp_http_client上下文打开一个新的套接字来实现。
 * 该函数接受一个esp_http_client句柄作为参数，根据操作结果返回不同的错误码。
 * 
 * @param https_client_handle 
 * @return OPERATE_RET 
 */
OPERATE_RET bull_hal_net_http_client_cancel_request( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_cancel_request( (esp_http_client_handle_t)https_client_handle ) );
}

/**
 * 设置HTTP客户端的URL
 * 
 * 本函数用于通过提供的URL来配置HTTP客户端的请求目标。它依赖于底层的HTTP客户端库来实现。
 * 
 * @param https_client_handle 指向HTTPS客户端句柄的指针。该句柄用于标识一个HTTP客户端实例。
 * @param url 指向以NULL结尾的URL字符串的指针。该URL指定客户端将要请求的资源位置。
 * 
 * @return 函数返回一个操作结果代码，成功时返回OPRT_OK，初始化未完成时返回OPRT_NOT_INIT，
 *         其他错误情况返回对应的错误码。
 */
OPERATE_RET bull_hal_net_http_client_set_url( HTTPS_CLIENT_HANDLE *https_client_handle, const char *url )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_url( (esp_http_client_handle_t)https_client_handle, url ) );
}


/**
 * 设置HTTP客户端的POST字段
 * 本函数用于配置HTTP客户端在发送POST请求时的字段内容。
 * 
 * @param https_client_handle 指向HTTPS客户端句柄的指针，用于标识特定的HTTP客户端实例。
 * @param data 要设置的POST字段的数据指针。
 * @param len POST字段数据的长度。
 * @return 函数执行状态码，成功返回OPRT_OK，失败返回相应的错误码。
 */
OPERATE_RET bull_hal_net_http_client_set_post_field( HTTPS_CLIENT_HANDLE *https_client_handle, const char *data, int len )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_post_field( (esp_http_client_handle_t)https_client_handle, data, len ) );
}


/**
 * @brief 设置HTTP客户端的请求头
 * 
 * 该函数用于在HTTP客户端的请求中添加、修改或删除指定的头字段。
 * 
 * @param https_client_handle 指向HTTPS客户端句柄的指针。
 * @param key 指定的头字段名称。
 * @param value 头字段的值。
 * @return OPERATE_RET 函数执行状态码，成功返回OPRT_OK，失败返回相应的错误码。
 */
OPERATE_RET bull_hal_net_http_client_set_header( HTTPS_CLIENT_HANDLE *https_client_handle, const char *key, const char *value )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_header( (esp_http_client_handle_t)https_client_handle, key, value ) );
}


OPERATE_RET bull_hal_net_http_client_set_username( HTTPS_CLIENT_HANDLE *https_client_handle, const char *username )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_username( (esp_http_client_handle_t)https_client_handle, username ) );
}

OPERATE_RET bull_hal_net_http_client_set_password( HTTPS_CLIENT_HANDLE *https_client_handle, const char *password )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_password( (esp_http_client_handle_t)https_client_handle, password ) );
}


OPERATE_RET bull_hal_net_http_client_set_user_data( HTTPS_CLIENT_HANDLE *https_client_handle, void **data )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_user_data( (esp_http_client_handle_t)https_client_handle, data ) );
}

OPERATE_RET bull_hal_net_http_client_get_user_data( HTTPS_CLIENT_HANDLE *https_client_handle, void **data )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_get_user_data( (esp_http_client_handle_t)https_client_handle, data ) );
}


OPERATE_RET bull_hal_net_http_client_set_method( HTTPS_CLIENT_HANDLE *https_client_handle, BULL_HTTP_CLIENT_METHOD_EUNM method )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_method( (esp_http_client_handle_t)https_client_handle, ( esp_http_client_method_t )method ) );
}


OPERATE_RET bull_hal_net_http_client_set_timeout_ms( HTTPS_CLIENT_HANDLE *https_client_handle, int timeout_ms )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_set_timeout_ms( (esp_http_client_handle_t)https_client_handle, timeout_ms ) );
}


OPERATE_RET bull_hal_net_http_client_delete_header( HTTPS_CLIENT_HANDLE *https_client_handle, const char *key )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_delete_header( (esp_http_client_handle_t)https_client_handle, key ) );
}


OPERATE_RET bull_hal_net_http_client_open( HTTPS_CLIENT_HANDLE *https_client_handle, int write_len )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_open( (esp_http_client_handle_t)https_client_handle, write_len ) );
}


OPERATE_RET bull_hal_net_http_client_write( HTTPS_CLIENT_HANDLE *https_client_handle, const char *buffer, int len )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_write( (esp_http_client_handle_t)https_client_handle, buffer, len ) );
}


OPERATE_RET bull_hal_net_http_client_fetch_headers( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_fetch_headers( (esp_http_client_handle_t)https_client_handle ) );
}


OPERATE_RET bull_hal_net_http_client_is_chunked_response( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_is_chunked_response( (esp_http_client_handle_t)https_client_handle ) );
}


OPERATE_RET bull_hal_net_http_client_read( HTTPS_CLIENT_HANDLE *https_client_handle, char *buffer, int len )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_read( (esp_http_client_handle_t)https_client_handle, buffer, len ) );
}


OPERATE_RET bull_hal_net_http_client_get_content_length( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_get_content_length( (esp_http_client_handle_t)https_client_handle ) );
}

/**
 * 从HTTP客户端获取状态码
 * 
 * 本函数用于通过HTTP客户端句柄获取服务器返回的状态码。
 * 
 * @param https_client_handle 指向HTTPS客户端句柄的指针。
 * @return 返回操作结果，如果成功则返回状态码，如果失败则返回错误码。
 */
OPERATE_RET bull_hal_net_http_client_get_status_code( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_get_status_code( (esp_http_client_handle_t)https_client_handle ) );
}

OPERATE_RET bull_hal_net_http_client_close( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_close( (esp_http_client_handle_t)https_client_handle ) );
}


OPERATE_RET bull_hal_net_http_client_cleanup( HTTPS_CLIENT_HANDLE *https_client_handle )
{
    if( NULL == https_client_handle ){

        return OPRT_NOT_INIT;
    }

    return  ( (OPERATE_RET)esp_http_client_cleanup( (esp_http_client_handle_t)https_client_handle ) );
}

/**
 * @brief 注册HTTPS的服务
 * 
 * @param reg_params 
 * @return OPERATE_RET 
 */
OPERATE_RET bull_hal_net_https_register( BULL_HTTPS_REGISTER_T * reg_params, HTTPS_CLIENT_HANDLE *https_client_handle )
{
    
    esp_http_client_config_t config = {    
        .method            = reg_params->method,
        .url               = reg_params->url,
        .event_handler     = bull_hal_https_event_handler,
        .crt_bundle_attach = esp_crt_bundle_attach,
        .transport_type    = HTTP_TRANSPORT_OVER_SSL,
        .user_data         = reg_params,
        .buffer_size       = 4*1024,
        .buffer_size_tx    = 1024,
        // .is_async          = true,

    };


    esp_http_client_handle_t client_handle = esp_http_client_init( &config );

    if( client_handle == NULL ){

        return OPRT_COM_ERROR;
    }

    *https_client_handle = client_handle;

    return OPRT_OK;
}
